package dsx.lock;

import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import dsx.inf.IBizStartModule;
import dsx.system.ThreadEx;

public class DustxLock extends ThreadEx implements IBizStartModule {

    private static final int checkInterval = 600000;
    private static int checkTimer = 0;
    private static final int checkThreshold = 100000;
    private final static Object syncObj = new Object();
    private static ConcurrentHashMap<String, _LockValue> rowLockMap = new ConcurrentHashMap<String, _LockValue>();
    private static ConcurrentHashMap<String, _LockValue> collectionLockMap = new ConcurrentHashMap<String, _LockValue>();

    private static void addRowLock(String collectionKey, String rowKey,
                                   String ownerKey, int lockTimeout) {
        long timeout = new Date().getTime() + lockTimeout;
        String key = collectionKey + rowKey;
        _LockValue value;
        if (rowLockMap.containsKey(key)) {
            value = rowLockMap.get(key);
            value.timeout = timeout;
        } else {
            value = new _LockValue(1, key, ownerKey, timeout);
            rowLockMap.put(key, value);
        }
    }

    public static boolean lockRows(String collectionKey, List<String> rowKeys,
                                   String ownerKey, int lockTimeout) {
        synchronized (syncObj) {
            long now = new Date().getTime();
            if (collectionLockMap.containsKey(collectionKey)) {
                _LockValue lockValue = collectionLockMap.get(collectionKey);
                if (lockValue.timeout > now)
                    return false;
            }
            for (String rowKey : rowKeys) {
                String key = collectionKey + rowKey;
                if (rowLockMap.containsKey(key)) {
                    _LockValue lockValue = rowLockMap.get(key);
                    if (!lockValue.ownerKey.equals(ownerKey)
                            && lockValue.timeout > now) {
                        return false;
                    }
                }
            }
            for (String rowKey : rowKeys) {
                addRowLock(collectionKey, rowKey, ownerKey, lockTimeout);
            }
            return true;
        }
    }

    public static boolean lockRows(List<String> collectionKeys,
                                   List<String> rowKeys, String ownerKey, int lockTimeout) {
        synchronized (syncObj) {
            long now = new Date().getTime();
            if (collectionKeys.size() != rowKeys.size())
                return false;
            for (int i = 0; i < collectionKeys.size(); i++) {
                String collectionKey = collectionKeys.get(i);
                if (collectionLockMap.containsKey(collectionKey)) {
                    _LockValue lockValue = collectionLockMap.get(collectionKey);
                    if (lockValue.timeout > now)
                        return false;
                }
                String rowKey = collectionKey + rowKeys.get(i);
                if (rowLockMap.containsKey(rowKey)){
                    _LockValue lockValue = rowLockMap.get(rowKey);
                    if (!lockValue.ownerKey.equals(ownerKey)
                            && lockValue.timeout > now) {
                        return false;
                    }
                }
            }
            for (int i = 0; i < collectionKeys.size(); i++) {
                addRowLock(collectionKeys.get(i), rowKeys.get(i), ownerKey,
                        lockTimeout);
            }
            return true;
        }
    }

    // / Warning: 集合锁没有判断是否有行锁，所以在使用时，获取成功以后，建议等待1000毫秒再开始后续操作。
    // / 不做与行锁的直接出斥，原因是行锁频繁请求的情况下，集合锁有可能长时间无法锁成功。
    // / ！！！慎用集合锁，需要使用集合锁的操作，建议在行锁不频繁的时间段进行。
    public static boolean lockCollections(List<String> collectionKeys,
                                          String ownerKey, int lockTimeout) {
        synchronized (syncObj) {
            long now = new Date().getTime();
            for (String collectionKey : collectionKeys) {
                if (collectionLockMap.containsKey(collectionKey)) {
                    _LockValue lockValue = collectionLockMap.get(collectionKey);
                    if (!lockValue.ownerKey.equals(ownerKey)
                            && lockValue.timeout > now) {
                        return false;
                    }
                }
            }

            long timeout = now + lockTimeout;
            for (String collectionKey : collectionKeys) {
                _LockValue value;
                if (collectionLockMap.containsKey(collectionKey)) {
                    value = collectionLockMap.get(collectionKey);
                    value.timeout = timeout;
                } else {
                    value = new _LockValue(0, collectionKey, ownerKey, timeout);
                    collectionLockMap.put(collectionKey, value);
                }
            }
            return true;
        }
    }

    public static void releaseRows(String collectionKey, List<String> rowKeys,
                                   String ownerKey) {
        synchronized (syncObj) {
            for (String rowKey : rowKeys) {
                String key = collectionKey + rowKey;
                if (rowLockMap.containsKey(key)) {
                    _LockValue lockValue = rowLockMap.get(key);
                    if (lockValue.ownerKey.equals(ownerKey)) {
                        rowLockMap.remove(key);
                    }
                }
            }
        }
    }

    public static void releaseRows(List<String> collectionKeys,
                                   List<String> rowKeys, String ownerKey) {
        synchronized (syncObj) {
            if (collectionKeys.size() != rowKeys.size())
                return;
            for (int i = 0; i < collectionKeys.size(); i++) {
                String collectionKey = collectionKeys.get(i);
                String key = collectionKey + rowKeys.get(i);
                if (rowLockMap.containsKey(key)) {
                    _LockValue lockValue = rowLockMap.get(key);
                    if (lockValue.ownerKey.equals(ownerKey)) {
                        rowLockMap.remove(key);
                    }
                }

            }
        }
    }

    public static void releaseCollections(List<String> collectionKeys,
                                          String ownerKey) {
        synchronized (syncObj) {
            synchronized (syncObj) {
                for (String collectionKey : collectionKeys)
                    if (collectionLockMap.containsKey(collectionKey)) {
                        _LockValue lockValue = collectionLockMap
                                .get(collectionKey);
                        if (lockValue.ownerKey.equals(ownerKey)) {
                            collectionLockMap.remove(collectionKey);
                        }
                    }
            }
        }
    }

    @Override
    protected void doInLoop() {
        try {
            ThreadEx.sleepEx(100);
            checkTimer += 100;
            if (checkTimer >= checkInterval) {
                long now = new Date().getTime();
                synchronized (syncObj) {
                    if (rowLockMap.size() >= checkThreshold) {
                        for (Entry<String, _LockValue> entry : rowLockMap
                                .entrySet()) {
                            _LockValue tmp = entry.getValue();
                            if (tmp.timeout <= now) {
                                rowLockMap.remove(tmp.key);
                            }
                        }
                    }

                    if (collectionLockMap.size() >= checkThreshold) {
                        for (Entry<String, _LockValue> entry : collectionLockMap
                                .entrySet()) {
                            _LockValue tmp = entry.getValue();
                            if (tmp.timeout <= now) {
                                collectionLockMap.remove(tmp.key);
                            }
                        }
                    }

                }
                checkTimer = 0;
            }
        } catch (Exception ex) {

        }
    }

    private boolean initialized = false;

    public void initialize() {
        if (!initialized) {
            setInterval(0);
            start();
            initialized = true;
        }
    }

    public void finalize() {
        if (initialized) {
            stopEx();
            initialized = false;
        }
    }
}
